package com.nami.pchat.domain.service.impl;

import com.github.yitter.idgen.YitIdHelper;
import com.google.protobuf.ByteString;
import com.nami.gate.external.basic.bo.ConnectedUserBO;
import com.nami.gate.external.basic.cache.user.UserDeviceRel;
import com.nami.gate.external.basic.event.EventPublisher;
import com.nami.gate.external.basic.event.enums.EventType;
import com.nami.gate.external.basic.event.factory.EventFactory;
import com.nami.gate.external.basic.event.model.CommonEvent;
import com.nami.im.network.model.enums.DownMsgErrorCode;
import com.nami.im.network.model.message.v2.Message;
import com.nami.pchat.domain.service.PrivateChatDomainService;
import com.nami.pchat.types.constants.CommonConst;
import com.nami.pchat.types.utils.MsgUtil;
import com.nami.transport.proto.codec.MqTransMsg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.enums.user.UserStatusEnum;
import org.dromara.common.core.utils.ConverUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisTemplateKit;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import static org.dromara.common.core.constant.RedisConst.CLIENT_MESSAGE_ID_EXPIRE_TIME;

@Slf4j
@Component
@RequiredArgsConstructor
public class PrivateChatDomainServiceImpl implements PrivateChatDomainService {

    private final RedisTemplateKit redisTemplateKit;
    private final EventPublisher eventPublisher;

    private boolean checkUserExist(UserDeviceRel senderUser, UserDeviceRel recvUser) {
        if (senderUser == null || recvUser == null) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    private boolean checkUserBanned(UserDeviceRel user) {
        if (UserStatusEnum.BAN.getCode() == user.getStatus()) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    private boolean checkUserSameApp(UserDeviceRel senderUser, UserDeviceRel recvUser) {
        return senderUser.getAppKey().equals(recvUser.getAppKey());
    }

    @Override
    public boolean checkUserParamError(ConnectedUserBO sender,
                                       UserDeviceRel senderUser, UserDeviceRel recvUser,
                                       Message message) {
        if (!this.checkUserExist(senderUser, recvUser)) {
            eventPublisher.publish(assembleDispatcherEvent(sender, message, DownMsgErrorCode.USER_NOT_EXIST));
            return true;
        } // end null if

        if (this.checkUserBanned(senderUser)) {
            eventPublisher.publish(assembleDispatcherEvent(sender, message, DownMsgErrorCode.FROM_USER_BAN));
            return true;
        } // end ban if

        if (this.checkUserBanned(recvUser)) {
            eventPublisher.publish(assembleDispatcherEvent(sender, message, DownMsgErrorCode.TO_USER_BAN));
            return true;
        } // end permission if

        if (!this.checkUserSameApp(senderUser, recvUser)) {
            eventPublisher.publish(assembleDispatcherEvent(sender, message, DownMsgErrorCode.USER_APP_NOT_MATCH));
            return true;
        } // end if appKey equals
        return false;
    }

    private CommonEvent assembleDispatcherEvent(ConnectedUserBO sender,
                                                Message message,
                                                DownMsgErrorCode downMsgErrorCode) {
        MqTransMsg.Logic2DispatchBody logic2DispatchBody = MqTransMsg.Logic2DispatchBody.newBuilder()
            .setSender(userCO2MqTransUser(sender))
            .setMessage(ByteString.copyFromUtf8(JsonUtils.toJsonString(message)))
            .setSuccess(Boolean.FALSE)
            .setErrCode(downMsgErrorCode.getCode())
            .setErrMessage(downMsgErrorCode.getMsg())
            .build();
        return EventFactory.newEvent(EventType.SEND_DISPATCHER, logic2DispatchBody.toByteArray());
    }

    private static MqTransMsg.TransUser userCO2MqTransUser(ConnectedUserBO sender) {
        MqTransMsg.TransUser senderUser = MqTransMsg.TransUser.newBuilder()
            .setAppKey(sender.getAppKey())
            .setUserId(sender.getUserId())
            .setPlatform(sender.getPlatform())
            .setConnectId(sender.getConnectId())
            .setInstanceId(sender.getInstanceId())
            .build();
        return senderUser;
    }

    private Long incrSeq(String appKey, String cid) {
        return redisTemplateKit.incr(MsgUtil.generateConverSeqKey(appKey, cid), CommonConst.BASE_NUM_1);
    }


    private void publishMessageService(MqTransMsg.Logic2DispatchBody trans, String senderId, String clientUid) {
        // 存储消息事件
        eventPublisher.publish(EventFactory.newEvent(EventType.STORE_PRIVATE_MSG, trans));
        MqTransMsg.TransUser sender = trans.getSender();

        this.storeIdempotencyParam(sender.getAppKey(), senderId, clientUid);
    }

    private boolean storeIdempotencyParam(String appKey, String userId, String clientMsgId) {
        if (StringUtils.isBlank(appKey) || StringUtils.isBlank(clientMsgId)) {
            return Boolean.FALSE;
        }
        redisTemplateKit.set(MsgUtil.genRepeatMsgKey(appKey, userId, clientMsgId),
            CommonConst.BASE_NUM_1, CLIENT_MESSAGE_ID_EXPIRE_TIME);
        return Boolean.TRUE;
    }

    private void publishDispatcherService(MqTransMsg.Logic2DispatchBody trans) {
        eventPublisher.publish(EventFactory.newEvent(EventType.SEND_DISPATCHER, trans));
    }

    @Override
    public boolean validateParamsNull(Message message) {
        if (message == null
            || StringUtils.isBlank(message.getMsgContent())
            || StringUtils.isBlank(message.getTargetId())
            || StringUtils.isBlank(message.getClientUid())) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }


    /**
     * 刷新会话
     *
     * @param trans
     */
    private void publishConverService(MqTransMsg.Logic2DispatchBody trans) {
        eventPublisher.publish(EventFactory.newEvent(EventType.UPD_PRIVATE_CONVERS, trans));
    }

    @Override
    public void publishMessageEvent(Integer qos,
                                    ConnectedUserBO sender,
                                    Message originalAppData
    ) {
        // 是否变为一个消息。
        MqTransMsg.Logic2DispatchBody trans = newMqTransBody(qos, sender, originalAppData);
        // 消息落库 异步
        this.publishMessageService(trans, sender.getUserId(), originalAppData.getClientUid());
        // 刷新会话，异步
        this.publishConverService(trans);
        // 转发消息事件 异步
        this.publishDispatcherService(trans);
    }

    @Override
    public void fillParamForThisMessage(String appKey, Message message) {
        // seq
        Long maxSeq = this.incrSeq(appKey,
            ConverUtils.generateCid(message.getSenderId(), message.getTargetId(), false));

        message.setMsgSeqNo(maxSeq);
        message.setMsgId(YitIdHelper.nextId() + "");
    }

    private static @NotNull MqTransMsg.Logic2DispatchBody newMqTransBody(Integer qos,
                                                                         ConnectedUserBO sender,
                                                                         Message message) {

        MqTransMsg.Logic2DispatchBody logic2DispatchBody = MqTransMsg.Logic2DispatchBody.newBuilder()
            .setSender(userCO2MqTransUser(sender))
            .setMessage(ByteString.copyFromUtf8(JsonUtils.toJsonString(message)))
            .setSuccess(Boolean.TRUE)
            .setQos(qos)
            .build();
        return logic2DispatchBody;
    }

}
